package top.codedance.iotp.client.plugins.async.udp;

import cd.io.netty.channel.ChannelHandlerContext;
import com.alibaba.fastjson.JSONObject;
import top.codedance.iotp.client.ClientApplication;
import top.codedance.iotp.common.executer.AsyncAbstractExecuter;
import top.codedance.iotp.common.entity.CommonEntity;
import org.apache.log4j.Logger;
import org.ini4j.Ini;
import org.ini4j.Wini;

import javax.xml.bind.DatatypeConverter;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class UdpExecuterImpl extends AsyncAbstractExecuter {
    private Logger logger = ClientApplication.getLogger(UdpExecuterImpl.class);
    private static ExecutorService udpPools = Executors.newCachedThreadPool();
    public static Map<Integer, UdpService> udps = new HashMap<>();
    public static volatile Map<Integer, Set<String>> tasks = new HashMap<>();

    @Override
    public void exec(ChannelHandlerContext channelHandlerContext, CommonEntity commonEntity, String taskId) {
        super.exec(channelHandlerContext, commonEntity, taskId);
        udpPools.submit(() -> {
            JSONObject requestBodyJSONObject = getRequestBodyJSONObject();
            Integer instance = requestBodyJSONObject.getInteger("instance");
            tryCreateConnect(this, instance);
            if(requestBodyJSONObject.containsKey("body")){
                synchronized (UdpExecuterImpl.class) {
                    try {
                        JSONObject body = requestBodyJSONObject.getJSONObject("body");
                        String command = body.getString("command");
                        UdpService dupService = udps.get(instance);
                        boolean isHex = false;
                        if(body.containsKey("hex")){
                            isHex = body.getBoolean("hex");
                        }
                        if(isHex){
                            dupService.send(DatatypeConverter.parseHexBinary(command));
                        }else{
                            dupService.send(command);
                        }

                        if(!tasks.containsKey(getInstance())){
                            tasks.put(getInstance(), new LinkedHashSet<>());
                        }
                        tasks.get(getInstance()).add(getClientId());
                    }catch (Exception e){
                        logger.debug(e.getMessage());
                    }
                }
            }
            if(requestBodyJSONObject.containsKey("release") && requestBodyJSONObject.getBoolean("release")){
                synchronized (UdpExecuterImpl.class) {
                    Set<Map.Entry<Integer, Set<String>>> entries = tasks.entrySet();
                    for (Map.Entry<Integer, Set<String>> entry : entries) {
                        try {
                            entry.getValue().remove(getClientId());
                        } catch (Exception e) {
                        }
                    }
                }
            }
        });
    }

    private synchronized void tryCreateConnect(AsyncAbstractExecuter executer, Integer instance){
        if(!udps.containsKey(instance)) {
            UdpServerParam udpServerParam = new UdpServerParam();
            Wini ini = getIniConfig();
            if (ini != null && ini.containsKey("udp")) {
                Ini.Section section = ini.get("udp");
                if (section.containsKey("ip." + instance)) {
                    udpServerParam.setIp(section.get("ip." + instance));
                }
                if (section.containsKey("port." + instance)) {
                    udpServerParam.setPort(Integer.parseInt(section.get("port." + instance)));
                }
            }
            UdpService udpService = new UdpService(executer, udpServerParam, instance);
            try{
                udpService.createConnect();
                udps.put(instance, udpService);
            }catch (Exception e){}
        }
    }
}
